home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Snippets / Sound / SoundLevel / SoundLevel.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-15  |  26.9 KB  |  1,188 lines  |  [TEXT/MPS ]

  1. /*    ---------------------------------------------------------------------------------
  2.     SoundLevel
  3.     
  4.     Ken Bereskin
  5.     March 30, 1992
  6.     
  7.     A simple application that echos the sound input level on the microphone using
  8.     a display much like a VCR peak-hold bar meter.
  9.     
  10.     Its a fun app, to use up those wasted background cycles and seems to amuse
  11.     people to no end. At 40k, its not a major memory hog so pop it in your Startup
  12.     Items folder and enjoy.
  13.     
  14.     The About Box introduces a new power user short cut. Clap hard enough to raise
  15.     the sound threshold about ≈ 95% and the dialog will dismiss. No need to reach
  16.     and strain for that enter key anymore!
  17.     
  18.     Caveats:
  19.         • Not much error checking.
  20.         • Too many globals.
  21.         • Saves the window position back to the app resource fork.
  22.         • Requires a Sound Input Device (MacRecorder will do just fine)
  23.         • It keeps the sound input device open with r/w so you won’t be able
  24.           to use other sound input apps unless you quit.
  25.     
  26.     Have fun and let me know what you think.      
  27.     
  28.     Copyright ©1992 Apple Canada Inc.
  29. */
  30.  
  31. /*    ---------------------------------------------------------------------------------
  32.     ToolBox header files
  33. */
  34. #include <Types.h>
  35. #include <QuickDraw.h>
  36. #include <Resources.h>
  37. #include <Windows.h>
  38. #include <Fonts.h>
  39. #include <Events.h>
  40. #include <TextEdit.h>
  41. #include <Dialogs.h>
  42. #include <Menus.h>
  43. #include <Memory.h>
  44. #include <Desk.h>
  45. #include <ToolUtils.h>
  46. #include <OSUtils.h>
  47. #include <Errors.h>
  48. #include <Folders.h>
  49. #include <OSEvents.h>
  50. #include <SegLoad.h>
  51. #include <Traps.h>
  52. #include <SoundInput.h>
  53. #include <GestaltEqu.h>
  54.  
  55.  
  56. /*    ---------------------------------------------------------------------------------
  57.     Forward reference prototypes
  58. */
  59. void    Initialize(void);
  60. void    SetupEnvironment(void);
  61. void    SetupMemory(void);
  62. void    SetupMenus(void);
  63. void    DoEvents(void);
  64. void    DoMouseDown (EventRecord *event);
  65. void    DoKeyDown (EventRecord *event);
  66. void    DoUpdateWindow (WindowPtr window);
  67. void    DoActivateWindow (Boolean activate, WindowPtr window);
  68. void    DoMenuCommand(short theMenu, short theItem);
  69. void    DoContentClick(EventRecord *event, WindowPtr window);
  70. void    DoSuspend(EventRecord *event);
  71. void    DoResume(EventRecord *event);
  72. void    DoIdle(void);
  73. void    DoDeskAccessory(short whichMenu, short whichItem);
  74. void    NewLevelWindow(void);
  75. void    CleanUp(void);
  76. void    DoAbout(void);
  77. Boolean    OptionTest(void);
  78. Boolean CmdTest(void);
  79. Boolean ShiftTest(void);
  80. Boolean    ColourQDExists (void);
  81. Boolean ColourQDIsOn (void);
  82. short    ColourDepth (void);
  83. void     TwitchToFinder(void);
  84. void    DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour);
  85. void     GetBarRect(Rect *meterRect, short whichBar, Rect *barRect);
  86. void     InitLevelMeter (void);
  87. void    CloseLevelMeter (void);
  88. OSErr     OpenTheSoundDevice(void);
  89. void     DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour);
  90. pascal char AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item);
  91. void     HiliteItem(DialogPtr dialog, short theitem);
  92. void    UpdateAboutDialog(WindowPtr window);
  93.  
  94. /*    ---------------------------------------------------------------------------------
  95.     Constants
  96. */
  97. #define kMaxVolume                255
  98. #define kPeakTimeoutTicks        60            // number of ticks needed to timeout peak
  99.  
  100. #define kMenuBarID    128
  101.  
  102. #define kAppleMenu        128
  103. #define kAboutItem        1
  104.  
  105. #define kFileMenu        129
  106. #define kQuitItem        1
  107.  
  108. #define kGestaltLevelMeter    'SnLv'
  109.  
  110. /*    ---------------------------------------------------------------------------------
  111.     Global variables
  112. */
  113. Boolean        WNEIsImplemented;                // is WaitNextEvent avaiable
  114. Boolean     gQuitFlag = false;                // set to force a quit
  115. WindowPtr     gLevelWindow;                    // sound level window pointer
  116. RGBColor    blueRGB, redRGB, blackRGB;        // rgb colours used for drawing meter
  117. short        gNumElements;                    // number of elements in the meter
  118. long        gPeakTimeOut;
  119. short        gPeakLevel;
  120. short         gLastLevel;
  121. short        gRedZone;
  122. Rect        gMeterRect;
  123. long        gSoundInputRefNum;
  124. Boolean        gUseColour;
  125.  
  126. /*    ---------------------------------------------------------------------------------
  127.     Main
  128.     
  129.     Application main entry point
  130. */
  131. main()
  132. {
  133.     Initialize();                    // do application initialization
  134.         
  135.     NewLevelWindow();                // create the level window
  136.     InitLevelMeter();                // start up the level meter
  137.  
  138.     DoUpdateWindow(gLevelWindow);
  139.     
  140.     while (!gQuitFlag)                // handle events until quit
  141.         DoEvents();
  142.  
  143.     CleanUp();                        // clean up before quitting
  144. }
  145.  
  146. /*    ---------------------------------------------------------------------------------
  147.     Intialize
  148.     
  149.     First time application initialization
  150. */
  151. #pragma segment Initialize
  152.  
  153. static void
  154. Initialize()
  155. {
  156.     InitGraf(&qd.thePort);
  157.     InitFonts();
  158.     InitWindows();
  159.     InitMenus();
  160.     TEInit();
  161.     InitDialogs(nil);
  162.     InitCursor();
  163.     
  164.     FlushEvents(everyEvent, 0);
  165.         
  166.     SetupMemory();
  167.     SetupEnvironment();
  168.     SetupMenus();
  169.     
  170.     gUseColour = ColourDepth() >= 4;
  171.     
  172. }
  173.  
  174. /*    ---------------------------------------------------------------------------------
  175.     SetupEnvironment
  176.     
  177.     This routine will determine the hardware and software operating environment
  178. */
  179.  
  180. #define RequestedVersion    1
  181.  
  182. static void
  183. SetupEnvironment()
  184. {
  185.     WNEIsImplemented = true;
  186. }
  187.  
  188. /*
  189.     --- SetupMemory ------------------------------------------------------------------------------------
  190.     This routine will allocate some master pointers, grow the heap,
  191.     and set up a grow zone function.
  192. */
  193. static void
  194. SetupMemory()
  195. {
  196.     MaxApplZone();
  197.     MoreMasters();
  198.     MoreMasters();
  199. }
  200.  
  201.  
  202. /*
  203.     --- SetupMenus ------------------------------------------------------------------------------------
  204.     This routine will set up the LeadSheet menus.
  205. */
  206.  
  207. static void
  208. SetupMenus()
  209. {
  210.     Handle menuBarHndl;
  211.     
  212.     // load up the apple and file menu
  213.     menuBarHndl = GetNewMBar(kMenuBarID);        // read menus into menu bar
  214.     SetMenuBar(menuBarHndl);                    // install menus
  215.     DisposHandle(menuBarHndl);
  216.     
  217.     AddResMenu(GetMHandle(kAppleMenu), 'DRVR');        // add DA names to Apple menu
  218.     
  219.     DrawMenuBar();
  220. }
  221.  
  222. #pragma segment Main
  223.  
  224. /*    ---------------------------------------------------------------------------------
  225.     DoEvents
  226.     
  227.     This is the LaunchPad Main Event Loop. We fetch events from the event queue, 
  228.     and call the proper handlers for the given event class. 
  229. */
  230.  
  231. #define kSleepTicks    0
  232.  
  233. static void
  234. DoEvents()
  235. {
  236.     Boolean newEvent;                // is there an event in the queue
  237.     EventRecord event;                // the event record    
  238.  
  239.     /*
  240.         Look for an event in the event queue.
  241.     */
  242.     if (WNEIsImplemented) {
  243.         newEvent = WaitNextEvent(everyEvent, &event, kSleepTicks, nil);
  244.     } else {
  245.         SystemTask();
  246.         newEvent = GetNextEvent(everyEvent, &event);
  247.     }
  248.         
  249.     DoIdle();            // idle processing
  250.     
  251.     /*    Dispatch on the type of event */    
  252.     switch(event.what) {
  253.         case mouseDown:
  254.             DoMouseDown(&event);
  255.             break;
  256.         case keyDown:
  257.         case autoKey:
  258.             DoKeyDown(&event);
  259.             break;
  260.         case updateEvt:
  261.             DoUpdateWindow((WindowPtr)event.message);
  262.             break;
  263.         case activateEvt:
  264.             DoActivateWindow(event.modifiers & activeFlag, (WindowPtr)event.message);
  265.             break;
  266.         case diskEvt:
  267.             break;
  268.         case osEvt:
  269.             /*
  270.                 Check for suspend or resume events (the only ones now defined)
  271.             */
  272.             if ((event.message >> 24) == suspendResumeMessage) {
  273.                 if (event.message & resumeFlag)
  274.                     DoResume(&event);
  275.                 else
  276.                     DoSuspend(&event);
  277.             }
  278.             break;
  279.         case highLevelEventMask:
  280.             break;
  281.     }
  282.     
  283.     DoIdle();            // more idle processing
  284. }
  285.  
  286. /*    --- DoMouseDown ----------------------------------------------------------------------------------
  287.     This routine handles mouseDown events.
  288. */
  289. static void
  290. DoMouseDown(event)
  291. EventRecord *event;
  292. {
  293.     short whichPart;
  294.     WindowPtr window;
  295.     Rect dragBounds;
  296.     long selection;
  297.     short theMenu, theItem;
  298.     
  299.     /*
  300.         Determine where the mouseDown event occurred
  301.     */
  302.     whichPart = FindWindow(event->where, &window);
  303.     
  304.     switch(whichPart) {
  305.         case inMenuBar:
  306.             selection = MenuSelect(event->where);
  307.             theMenu = HiWord(selection);
  308.             theItem = LoWord(selection);
  309.             DoMenuCommand(theMenu, theItem);
  310.             break;
  311.         case inSysWindow:
  312.             SystemClick(event, window);
  313.             break;
  314.         case inContent:
  315.             DoContentClick(event, window);
  316.             break;
  317.         case inDrag:
  318.             dragBounds = qd.screenBits.bounds;
  319.             DragWindow(window, event->where, &dragBounds);
  320.             break;
  321.         case inGrow:
  322.             break;
  323.         case inGoAway:
  324.             break;
  325.         case inZoomIn:
  326.         case inZoomOut:
  327.             break;
  328.         case inDesk:        // a click in the desktop will deselect the current selection
  329.             break;
  330.     }
  331. }
  332.  
  333.  
  334. /*    ---------------------------------------------------------------------------------
  335.     DoKeyDown
  336.     
  337.     This routine handles keyDown events.
  338. */
  339. static void
  340. DoKeyDown (EventRecord *event)
  341. {
  342. #pragma unused(event)
  343.     gQuitFlag = true;
  344. }
  345.  
  346. /*    ---------------------------------------------------------------------------------
  347.     DoUpdateWindow
  348.  
  349.     This routine handles update events for the given window
  350. */
  351.  
  352. static void
  353. DoUpdateWindow (window)
  354. WindowPtr window;
  355. {
  356.     GrafPtr savePort;
  357.     Rect boundsRect;
  358.     short i;
  359.     
  360.     GetPort(&savePort);
  361.     
  362.     BeginUpdate(window);        // set up the clipRgn and visRgn
  363.     
  364.     SetPort(window);
  365.     
  366.     RGBForeColor(&blackRGB);
  367.     EraseRect(&window->portRect);
  368.     boundsRect = window->portRect;
  369.     InsetRect(&boundsRect, 2, 2);
  370.     PaintRect(&boundsRect);
  371.     
  372.     // redraw the bar graph frames up to the current level
  373.     for (i = 1; i <= gLastLevel; i++) {
  374.         DrawOneBar(&gMeterRect, i, gUseColour);
  375.     }
  376.     
  377.     EndUpdate(window);            // restore window regions
  378.     
  379.     SetPort(savePort);
  380. }
  381.  
  382. /*    --- DoActivateWindow ----------------------------------------------------------------------------------
  383.     This routine handles activate events for the given window
  384. */
  385. static void
  386. DoActivateWindow (activate, window)
  387. Boolean activate;
  388. WindowPtr window;
  389. {
  390. #pragma unused(activate, window)
  391. }
  392.  
  393. /*    ---------------------------------------------------------------------------------
  394.     DoMenuCommand
  395.     
  396.     This routine handles menu selection dispatching
  397. */
  398.  
  399. static void
  400. DoMenuCommand(short theMenu, short theItem)
  401. {
  402.     switch (theMenu) {
  403.         case kAppleMenu:
  404.             if (theItem == kAboutItem) {
  405.                 DoAbout();
  406.             } else {
  407.                 DoDeskAccessory(theMenu, theItem);
  408.             }
  409.             break;
  410.         case kFileMenu:
  411.             if (theItem == kQuitItem) {
  412.                 ExitToShell();
  413.             }
  414.             break;
  415.         default:
  416.             break;
  417.     }
  418.     
  419.     HiliteMenu(0);
  420. }
  421.  
  422. /*    --- DoContentClick ------------------------------------------------------------------------------
  423.     This routine handles clicks in the content area of a window. If the option key is down,
  424.     drag the launch window to another location on the screen.
  425. */
  426. static void
  427. DoContentClick(event, window)
  428. EventRecord *event;
  429. WindowPtr window;
  430. {
  431. #pragma unused(event)
  432.  
  433.     GrafPtr savePort;
  434.     Point clickPt;
  435.     
  436.     /*
  437.         If the clicked window isn't frontmost, simply select it
  438.     */
  439.     if (window != FrontWindow()) {
  440.         SelectWindow(window);
  441.         return;
  442.     }
  443.     
  444.     GetPort(&savePort);                    // save the current port
  445.     SetPort(window);                    // set the port to the clicked window
  446.         
  447.     clickPt = event->where;                // convert point to local coordindates
  448.     GlobalToLocal(&clickPt);
  449.     
  450.     DragWindow(window, event->where, &qd.screenBits.bounds);
  451.         
  452.     SetPort(savePort);                    // restore the saved port
  453. }
  454.  
  455. /*    ---------------------------------------------------------------------------------
  456.     DoSuspend
  457.  
  458.     This routine handles multifinder suspend events.
  459. */
  460. static void
  461. DoSuspend(event)
  462. EventRecord *event;
  463. {
  464. #pragma unused(event)
  465.     DoActivateWindow(false, gLevelWindow);
  466. }
  467.  
  468. /*    ---------------------------------------------------------------------------------
  469.     DoResume
  470.     
  471.     This routine handles multifinder resume events.
  472. */
  473. static void
  474. DoResume(event)
  475. EventRecord *event;
  476. {
  477. #pragma unused(event)
  478.     InitCursor();
  479.     DoActivateWindow(true, gLevelWindow);
  480. }
  481.  
  482. /*    ---------------------------------------------------------------------------------
  483.     DoDeskAccessory
  484.     
  485.     Handle desk accessory selections from the apple menu
  486. */
  487.  
  488. static void
  489. DoDeskAccessory(whichMenu, whichItem)
  490. short whichMenu;
  491. short whichItem;
  492. {
  493.     Str255 daName;
  494.     
  495.     GetItem(GetMHandle(whichMenu), whichItem, daName);
  496.         
  497.     (void) OpenDeskAcc(daName);
  498. }
  499.  
  500. /*    ---------------------------------------------------------------------------------
  501.     NewLevelWindow
  502.  
  503.     This routine will create the sound level window.
  504. */
  505.  
  506. static void
  507. NewLevelWindow()
  508. {
  509.     Point **userOffsetPtHndl;
  510.     Rect screenRect;
  511.     Rect globalRect;
  512.     
  513.     if (ColourQDExists())
  514.         gLevelWindow = GetNewCWindow(128, nil, (WindowPtr)-1);
  515.     else
  516.         gLevelWindow = GetNewWindow(128, nil, (WindowPtr)-1);
  517.         
  518.     SetPort(gLevelWindow);
  519.     
  520.     // load the offset resource that tells us if the user has dragged
  521.     // the window somewhere else. make sure the window remains visible
  522.     // and eventually put this in a pref file where it belongs
  523.     
  524.     userOffsetPtHndl = (Point **)GetResource('WPos', 128);
  525.     if (userOffsetPtHndl != nil) {
  526.         screenRect = qd.screenBits.bounds;
  527.         InsetRect(&screenRect, 5, 5);        // a little bit of a margin
  528.         
  529.         // the port is set for our window, so convert (0,0) to global for the
  530.         // global topLeft of the window
  531.         globalRect.left = (**userOffsetPtHndl).h;
  532.         globalRect.top = (**userOffsetPtHndl).v;
  533.         globalRect.right = globalRect.left + (gLevelWindow->portRect.right - gLevelWindow->portRect.left);
  534.         globalRect.bottom = globalRect.top + (gLevelWindow->portRect.bottom - gLevelWindow->portRect.top);
  535.  
  536.         if (SectRect(&screenRect, &globalRect, &globalRect)) {
  537.             MoveWindow(gLevelWindow, globalRect.left, globalRect.top, true);
  538.         } 
  539.         
  540.         ReleaseResource((Handle)userOffsetPtHndl);
  541.     }
  542.     
  543.     ShowWindow(gLevelWindow);            // make it visible
  544. }
  545.  
  546. /*    ---------------------------------------------------------------------------------
  547.     CleanUp
  548.  
  549.     This routine will clean up before exiting to the Finder
  550. */
  551. static void
  552. CleanUp()
  553. {
  554.     Point **userOffsetPtHndl;
  555.     Point globalPt;
  556.     
  557.     CloseLevelMeter();                // close up the level meter
  558.  
  559.     // save the position of the top/left corner of the window in case the user
  560.     // has moved it. For now, back to the WPos resource in the app will have to do
  561.     userOffsetPtHndl = (Point **)GetResource('WPos', 128);
  562.     if (userOffsetPtHndl != nil) {
  563.         SetPort(gLevelWindow);
  564.         globalPt.h = globalPt.v = 0;
  565.         LocalToGlobal(&globalPt);
  566.         
  567.         **userOffsetPtHndl = globalPt;
  568.         ChangedResource((Handle)userOffsetPtHndl);
  569.         WriteResource((Handle)userOffsetPtHndl);
  570.     }
  571.     if (gLevelWindow != nil)
  572.         DisposeWindow(gLevelWindow);
  573. }
  574.  
  575. /*    ---------------------------------------------------------------------------------
  576.     MakeCursor
  577.     
  578.     This routine will set the cursor to the 'CURS' resource whose id is given.
  579. */
  580. void
  581. MakeCursor(cursorID)
  582. short cursorID;
  583. {
  584. #pragma unused(cursorID)
  585. }
  586.  
  587.  
  588. /*    ---------------------------------------------------------------------------------
  589.     DoIdle
  590.     
  591. */
  592. static void
  593. DoIdle()
  594. {
  595.     OSErr err;
  596.     short recordingStatus = 0;                        // status of recording session
  597.     short meterLevel = 0;                            // current meter level
  598.     unsigned long totalSamplesToRecord = 0;            // total number of samples
  599.     unsigned long numberOfSamplesRecorded = 0;        // number of samples recorded
  600.     unsigned long totalMsecsToRecord;
  601.     unsigned long numberOfMsecsRecorded;
  602.     GrafPtr savePort;
  603.     
  604.     GetPort(&savePort);
  605.     SetPort(gLevelWindow);
  606.     
  607.     /* get the sound input status    */
  608.     err = SPBGetRecordingStatus(gSoundInputRefNum,
  609.                                 &recordingStatus,
  610.                                 &meterLevel,
  611.                                 &totalSamplesToRecord,
  612.                                 &numberOfSamplesRecorded,
  613.                                 &totalMsecsToRecord,
  614.                                 &numberOfMsecsRecorded);
  615.              
  616.     DrawTheMeter(&gMeterRect, 
  617.                    gNumElements, 
  618.                    &gPeakLevel, 
  619.                    &gLastLevel, 
  620.                    &gPeakTimeOut, 
  621.                    meterLevel,
  622.                    gRedZone,
  623.                    &blueRGB,
  624.                    &redRGB,
  625.                    &blackRGB,
  626.                    gUseColour);
  627.                    
  628.     SetPort(savePort);
  629. }
  630.  
  631. /*    ---------------------------------------------------------------------------------
  632.     OptionTest
  633.     
  634.     Returns whether the option key is being pressed
  635. */
  636. static Boolean
  637. OptionTest()
  638. {
  639.     KeyMap theKeys;
  640.     
  641.     GetKeys(&theKeys);
  642.     
  643.     if (theKeys[1] & 4)
  644.         return(true);
  645.     else
  646.         return(false);
  647.  
  648. }
  649.  
  650. /*    ---------------------------------------------------------------------------------
  651.     CmdTest
  652.     
  653.     Returns whether the command (apple) key is being pressed
  654. */
  655. static Boolean
  656. CmdTest()
  657. {
  658.     KeyMap theKeys;
  659.     
  660.     GetKeys(&theKeys);
  661.     
  662.     if (theKeys[1] & 0x8000)
  663.         return(true);
  664.     else
  665.         return(false);
  666.  
  667. }
  668.  
  669. /*    ---------------------------------------------------------------------------------
  670.     ShiftTest
  671.     
  672.     Returns whether the shift key is being pressed
  673. */
  674. static Boolean
  675. ShiftTest()
  676. {
  677.     KeyMap theKeys;
  678.     
  679.     GetKeys(&theKeys);
  680.     
  681.     if (theKeys[1] & 1)
  682.         return(true);
  683.     else
  684.         return(false);
  685.  
  686. }
  687.  
  688. #define ROM85            (*(short *)0x28e)
  689. #define TWOHIGHMASK        0xc000
  690. #define COLOURQDEXISTS    !(ROM85 & TWOHIGHMASK)
  691.  
  692.  
  693. /*    ---------------------------------------------------------------------------------
  694.     ColourQDExists
  695.     
  696.     This routine is called to determine whether or not color quickdraw
  697.     exists in the current roms. It checks the lom memory global just
  698.     like Apple usually does…
  699. */
  700. Boolean
  701. ColourQDExists()
  702. {
  703.     return(COLOURQDEXISTS);
  704. }
  705.  
  706. /*    ---------------------------------------------------------------------------------
  707.     ColourQDIsOn
  708.     
  709.     This routine is called to determine if colour quickdraw exists
  710.     in the current roms and if the main screen is displaying color.
  711. */
  712.  
  713. #define QD_MIN_FOR_COLOUR    4
  714.  
  715. Boolean
  716. ColourQDIsOn()
  717. {
  718.     GDHandle maindevice;
  719.     PixMapHandle mainpix;
  720.     short depth;
  721.  
  722.     /*
  723.         First confirm that color is available in the roms
  724.     */
  725.     if (COLOURQDEXISTS) {
  726.         /*
  727.             Determine if the main device is displaying color
  728.         */
  729.         maindevice = GetMainDevice();
  730.         mainpix = (*maindevice)->gdPMap;
  731.         
  732.         /*
  733.             Check the depth of the pixel map
  734.         */
  735.         depth = (*mainpix)->pixelSize;
  736.         
  737.         if (depth < QD_MIN_FOR_COLOUR)
  738.             return(false);
  739.         else
  740.             return(true);
  741.     } else
  742.         return(false);
  743. }
  744.  
  745. short
  746. ColourDepth()
  747. {
  748.     GDHandle maindevice;
  749.     PixMapHandle mainpix;
  750.  
  751.     /*
  752.         First confirm that color is available in the roms
  753.     */
  754.     if (COLOURQDEXISTS) {
  755.         /*
  756.             Determine if the main device is displaying color
  757.         */
  758.         maindevice = GetMainDevice();
  759.         mainpix = (*maindevice)->gdPMap;
  760.         
  761.         /*
  762.             Check the depth of the pixel map
  763.         */
  764.         return (*mainpix)->pixelSize;
  765.     } else
  766.         return 1;
  767. }
  768.  
  769. /*    ---------------------------------------------------------------------------------
  770.     TwitchToFinder
  771.     
  772.     Switch back to Finder. Use the System 7.0 process manager if present
  773. */
  774. static void
  775. TwitchToFinder()
  776. {
  777.  
  778. }
  779.  
  780. /*    ---------------------------------------------------------------------------------
  781.     DrawTheMeter
  782.     
  783.     This routine is called to draw the volume meter during sound input.
  784. */
  785.  
  786. void
  787. DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour)
  788. {
  789.     Rect barRect;
  790.     short i;
  791.     
  792.     PenNormal();
  793.     
  794.     // quantize the meterLevel based on number of elements
  795.     // in our bar chart
  796.     if (meterLevel > kMaxVolume)
  797.         meterLevel = kMaxVolume;
  798.         
  799.     meterLevel = (meterLevel * numElements) / kMaxVolume;
  800.     
  801.     // determine if this is a new peak value and erase the
  802.     // peak bar if it has timed out
  803.     if (meterLevel >= *peakLevel) {
  804.         *peakLevel = meterLevel;
  805.         *peakTimeout = TickCount() + kPeakTimeoutTicks; 
  806.     } else {
  807.         // has the current peak timed out?
  808.         if (*peakLevel > 0 && *peakTimeout <= TickCount()) {
  809.             if (*peakLevel >= *lastLevel) {
  810.             
  811.                 if (useColour)
  812.                     RGBForeColor(blackRGB);
  813.  
  814.                 GetBarRect(meterRect, *peakLevel, &barRect);
  815.                 PaintRect(&barRect);
  816.                 
  817.                 // erase the previous element (peak is two elements wide)
  818.                 if (*peakLevel > 1) {            // don’t erase a non-element
  819.                     GetBarRect(meterRect, *peakLevel - 1, &barRect);
  820.                     PaintRect(&barRect);
  821.                 }
  822.             }
  823.             *peakLevel = 0;
  824.         }
  825.     }
  826.         
  827.     // check whether the signal is now stronger than last time
  828.     if (meterLevel > *lastLevel) {
  829.         for (i = *lastLevel + 1; i <= meterLevel; i++) {
  830.             GetBarRect(meterRect, i, &barRect);                    // get the rect for this bar
  831.             
  832.             if (useColour) {                                    // watch out for colour
  833.                 if (i >= redZone)                                // are we beyond the clipping point?
  834.                     RGBForeColor(redRGB);                        // draw in red to show distortion
  835.                 else
  836.                     RGBForeColor(blueRGB);                        // draw in blue for normal signal
  837.                 PaintRect(&barRect);                            // fill the element in
  838.                 
  839.             } else
  840.                 EraseRect(&barRect);                            // no colour so white will have to do
  841.         }
  842.         *lastLevel = meterLevel;                                // remember for next time
  843.     }
  844.         
  845.     // check whether the signal level is now weaker than last time
  846.     // and if it is remove the rightmost two level bar currently on
  847.     if (meterLevel < *lastLevel) {
  848.         if (*peakLevel != 0) {
  849.             if (*lastLevel == *peakLevel)                // don’t erase the peak!
  850.                 *lastLevel -= 2;
  851.             if (*lastLevel == *peakLevel -1)
  852.                 *lastLevel -= 1;
  853.         }
  854.         
  855.         if (useColour)
  856.             RGBForeColor(blackRGB);
  857.  
  858.         for (i = 0; i < 2; i++) {
  859.             if (*lastLevel > 0) {
  860.                 GetBarRect(meterRect, *lastLevel, &barRect);
  861.                 PaintRect(&barRect);
  862.                 *lastLevel -= 1;
  863.             }
  864.             
  865.             if (*lastLevel < 0 )
  866.                 *lastLevel = 0;
  867.         }
  868.         
  869.     }
  870.  
  871.     if (useColour)
  872.         RGBForeColor(blackRGB);
  873.  
  874. }
  875.  
  876. /*    ---------------------------------------------------------------------------------
  877.     GetBarRect
  878. */
  879. void
  880. GetBarRect(Rect *meterRect, short whichBar, Rect *barRect)
  881. {
  882.     if (whichBar == 0) {
  883.         return;
  884.     }
  885.     
  886.     SetRect(barRect, meterRect->left + 2, 
  887.                      meterRect->top + 1, 
  888.                      meterRect->left + 4, 
  889.                      meterRect->bottom - 1);
  890.                      
  891.     OffsetRect(barRect, (whichBar - 1)* 3, 0);
  892. }
  893.  
  894. /*    -------------------------------------------------------------------------------
  895.     InitLevelMeter
  896. */
  897.  
  898. void
  899. InitLevelMeter()
  900. {
  901.     RGBColor **colorRsrcHndl;
  902.     OSErr err;
  903.     
  904.     // load the colours used for the bar chart
  905.     colorRsrcHndl = (RGBColor **)GetResource(128, 'RGBv');
  906.     if (colorRsrcHndl) {
  907.         blueRGB = **colorRsrcHndl;
  908.         ReleaseResource((Handle)colorRsrcHndl);
  909.     } else {
  910.         blueRGB.red = 39321;        // flourescant blue
  911.         blueRGB.green = 65535;
  912.         blueRGB.blue = 65535;
  913.     }
  914.     
  915.     colorRsrcHndl = (RGBColor **)GetResource(129, 'RGBv');
  916.     if (colorRsrcHndl) {
  917.         redRGB = **colorRsrcHndl;
  918.         ReleaseResource((Handle)colorRsrcHndl);
  919.     } else {
  920.         redRGB.red = 65535;            // peak level red
  921.         redRGB.green = 0;
  922.         redRGB.blue = 0;
  923.     }
  924.     
  925.     blackRGB.red = 0;
  926.     blackRGB.green = 0;
  927.     blackRGB.blue = 0;
  928.     
  929.     gMeterRect = gLevelWindow->portRect;
  930.     InsetRect(&gMeterRect, 2, 2);
  931.     
  932.     gNumElements = (gMeterRect.right - gMeterRect.left - 2) / 3;
  933.     if (gNumElements > 255)
  934.         gNumElements = 255;
  935.  
  936.     gPeakTimeOut = 0;
  937.     gPeakLevel = 0;
  938.     gLastLevel = 0;
  939.     gRedZone = (gNumElements * 2) / 3;
  940.  
  941.     // open the sound input device
  942.     err = OpenTheSoundDevice();
  943. }
  944.  
  945.  
  946. // watch out for other usages of the soundInputRefNum
  947.  
  948. static void
  949. CloseLevelMeter()
  950. {
  951.     OSErr err;
  952.     
  953.     err = SPBCloseDevice(gSoundInputRefNum);
  954.     gSoundInputRefNum = 0;
  955. }
  956.  
  957. #define kAboutDLOG            128
  958. #define kAboutMeterItem        4
  959.  
  960. short    gAboutPeakLevel;
  961. short    gAboutLastLevel;
  962. long    gAboutPeakTimeOut;
  963.  
  964. void
  965. DoAbout()
  966. {
  967.     DialogPtr dialog;
  968.     short itemHit;
  969.     GrafPtr savePort;
  970.     
  971.     GetPort(&savePort);
  972.     dialog = GetNewDialog(kAboutDLOG, nil, (WindowPtr)-1);
  973.     SetPort(dialog);
  974.     
  975.     // ••• force an update event first
  976.     UpdateAboutDialog((WindowPtr)dialog);
  977.     
  978.     gAboutPeakLevel = 0;
  979.     gAboutLastLevel = 0;
  980.     gAboutPeakTimeOut = 0;
  981.     
  982.     do {
  983.         ModalDialog((ModalFilterProcPtr)AboutFilterProc, &itemHit);
  984.     } while (itemHit != ok);
  985.     
  986.     SetPort(savePort);
  987.     DisposDialog(dialog);
  988.  
  989. }
  990.  
  991. OSErr OpenTheSoundDevice()
  992. {
  993.     OSErr err;
  994.     short meterState;
  995.     
  996.     // set the default zone to the system heap so that the SI manager will
  997.     // possibly get fooled into allocating there instead of my heap
  998.     
  999.     SetZone(SystemZone());
  1000.     err = SPBOpenDevice(nil, siWritePermission, &gSoundInputRefNum);
  1001.     if (err != noErr) 
  1002.         goto BailOut;
  1003.         
  1004.     // turn on sound metering
  1005.     meterState = 1;                // turn it on
  1006.     err = SPBSetDeviceInfo(gSoundInputRefNum, siLevelMeterOnOff, (char *)&meterState);
  1007.  
  1008. BailOut:
  1009.     SetZone(ApplicZone());
  1010.     
  1011.     return err;
  1012. }
  1013.  
  1014. void
  1015. DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour)
  1016. {
  1017.     Rect barRect;
  1018.     
  1019.     GetBarRect(meterRect, whichBar, &barRect);            // get the rect for this bar
  1020.     
  1021.     if (useColour) {                                    // watch out for colour
  1022.         if (whichBar >= gRedZone)                        // are we beyond the clipping point?
  1023.             RGBForeColor(&redRGB);                        // draw in red to show distortion
  1024.         else
  1025.             RGBForeColor(&blueRGB);                        // draw in blue for normal signal
  1026.         PaintRect(&barRect);                            // fill the element in
  1027.         
  1028.     } else
  1029.         EraseRect(&barRect);                            // no colour so white will have to do
  1030. }
  1031.  
  1032. /*    ---------------------------------------------------------------------------------
  1033.     AboutFilterProc
  1034.     
  1035. */
  1036.  
  1037. #define CRKey            0x0d
  1038. #define EnterKey         0x03
  1039. #define EscapeKey        0x1b
  1040.  
  1041. pascal char
  1042. AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item)
  1043. {
  1044.     unsigned char theKey;
  1045.     WindowPtr window;
  1046.     short itemType;
  1047.     Handle itemHndl;
  1048.     Rect itemRect;
  1049.     OSErr err;
  1050.     short recordingStatus = 0;                        // status of recording session
  1051.     short meterLevel = 0;                            // current meter level
  1052.     unsigned long totalSamplesToRecord = 0;            // total number of samples
  1053.     unsigned long numberOfSamplesRecorded = 0;        // number of samples recorded
  1054.     unsigned long totalMsecsToRecord;
  1055.     unsigned long numberOfMsecsRecorded;
  1056.         
  1057.     /* get the sound input status    */
  1058.     if (event->what != updateEvt) {
  1059.         err = SPBGetRecordingStatus(gSoundInputRefNum,
  1060.                                     &recordingStatus,
  1061.                                     &meterLevel,
  1062.                                     &totalSamplesToRecord,
  1063.                                     &numberOfSamplesRecorded,
  1064.                                     &totalMsecsToRecord,
  1065.                                     &numberOfMsecsRecorded);
  1066.                  
  1067.         GetDItem(dialog, kAboutMeterItem, &itemType, &itemHndl, &itemRect);
  1068.         InsetRect(&itemRect, 2, 2);
  1069.         
  1070.         SetPort(dialog);
  1071.         
  1072.         DrawTheMeter(&itemRect, 
  1073.                        gNumElements, 
  1074.                        &gAboutPeakLevel, 
  1075.                        &gAboutLastLevel, 
  1076.                        &gAboutPeakTimeOut, 
  1077.                        meterLevel,
  1078.                        gRedZone,
  1079.                        &blueRGB,
  1080.                        &redRGB,
  1081.                        &blackRGB,
  1082.                        gUseColour);
  1083.                        
  1084.         // check for nearly maximum volume to dismiss dialog;
  1085.         if (meterLevel > 250) {
  1086.             HiliteItem(dialog, ok);
  1087.             *item = ok;
  1088.             return true;
  1089.         }
  1090.         
  1091.     }
  1092.     
  1093.     switch(event->what) {
  1094.         case mouseDown:
  1095.             break;
  1096.         case keyDown:
  1097.         case autoKey:
  1098.             theKey = event->message & charCodeMask;
  1099.             
  1100.             if (theKey == CRKey || theKey == EnterKey || theKey == EscapeKey) {
  1101.                 HiliteItem(dialog, ok);
  1102.                 *item = ok;
  1103.                 return true;
  1104.             }
  1105.                         
  1106.             break;
  1107.         case updateEvt:
  1108.             window = (WindowPtr)event->message;
  1109.             if (window == (WindowPtr)dialog) {
  1110.                 UpdateAboutDialog(window);
  1111.             }
  1112.             break;
  1113.         default:
  1114.             break;
  1115.     }
  1116.     return false;
  1117. }
  1118.  
  1119. void
  1120. UpdateAboutDialog(WindowPtr window)
  1121. {
  1122.     GrafPtr savePort;
  1123.     short itemType;
  1124.     Handle itemHndl;
  1125.     Rect itemRect;
  1126.     
  1127.     BeginUpdate(window);
  1128.     GetPort(&savePort);
  1129.     SetPort(window);
  1130.     
  1131.     UpdtDialog((DialogPtr)window, window->visRgn);
  1132.     
  1133.     PenNormal();
  1134.     GetDItem((DialogPtr)window, kAboutMeterItem, &itemType, &itemHndl, &itemRect);
  1135.     FrameRect(&itemRect);
  1136.     InsetRect(&itemRect, 2, 2);
  1137.     PaintRect(&itemRect);
  1138.     
  1139.     SetPort(savePort);
  1140.     EndUpdate(window);
  1141. }
  1142.  
  1143. /*    ---------------------------------------------------------------------------------
  1144.     HiliteItem
  1145.     
  1146.     This routine will hilite the given item control so the user knows
  1147.     which item has been activated by the  key equivalent
  1148. */
  1149.  
  1150. #define ButtonInvertState    10        // inverted state for push buttons
  1151. #define CheckRadioState        11        // inverted state for checks and radios
  1152. #define NormalState            0        // non-inverted state
  1153. #define SleepDuration        10        // pause for 10 ticks
  1154.  
  1155. void
  1156. HiliteItem(DialogPtr dialog, short theitem)
  1157. {
  1158.     Handle thehndl;
  1159.     short thetype;
  1160.     Rect thebox;
  1161.     long junkTicks;
  1162.     short hiliteState;
  1163.     
  1164.     /*
  1165.         Get Handle to the item
  1166.     */
  1167.     GetDItem(dialog, theitem, &thetype, &thehndl, &thebox);
  1168.     
  1169.     switch (thetype) {
  1170.         case btnCtrl + ctrlItem:
  1171.             hiliteState = ButtonInvertState;
  1172.             break;
  1173.         case chkCtrl + ctrlItem:
  1174.         case radCtrl + ctrlItem:
  1175.             hiliteState = CheckRadioState;
  1176.             break;
  1177.         default:
  1178.             return;
  1179.             break;
  1180.     }
  1181.     /*
  1182.         Invert the control for a little while
  1183.     */
  1184.     HiliteControl((ControlHandle)thehndl, hiliteState);
  1185.     Delay(SleepDuration, &junkTicks);
  1186.     HiliteControl((ControlHandle)thehndl, NormalState);
  1187. }
  1188.